17

在用JSAPI开发微信支付的时候,遇到了很多坑,我也对朋友说过,一步一坑。最后终于算是走出来了。期间翻阅过很多网友写的教程,但是都不实用,JAVA,Python都有看过,大多数都是复制粘贴,倍感失望。

开发环境

thinkphp5.0 php

(开始使用JSAPI需要一个概念,就是在整个JSAPI的逻辑里面,只存在一个随机字符串 和一个 时间戳。相当于JSAPI类里的全局。)

注意:由于我这里用的http_build_query()body里面的中文会被格式化,所以这里先建议body使用非中文,至于中文解决,可以用正则去替换,或者不用http_build_query()

开始开发

全局初始化

 public function __construct($total_fee, $body, $openid)
    {
        $rand = rand(11, 99);
        $mp_info = get_mpid_info();//获取微信信息
        $this->appid = $mp_info['appid'];
        $this->nonce_str = nonceStr(32);
        $this->spbill_create_ip = Request::instance()->ip();
        $this->mch_id = $mp_info['mch_id'];
        $this->key = $mp_info['paykey'];
        $this->timestamp = time();
        $this->sign;//一次签名
        $this->total_fee = $total_fee;
        $this->out_trade_no = time() . $rand;
        $this->notify_url = 'http://uedream.cn/index.php';
        $this->body = $body;
        $this->openid = $openid;
        $this->sign_type = 'MD5';
        $this->createsign(); //生成签名方法,需要结合createsign方法
    }

以上是初始化签名结构体

获取签名

文档:https://pay.weixin.qq.com/wik...
public function createsign()
    {
        $build = [
            'appid' => $this->appid,
            'body' => $this->body,
            'mch_id' => $this->mch_id,
            'nonce_str' => $this->nonce_str,
            'notify_url' => $this->notify_url,
            'openid' => $this->openid,
            'out_trade_no' => $this->out_trade_no,
            'sign_type' => $this->sign_type,
            'spbill_create_ip' => $this->spbill_create_ip,
            'timeStamp' => $this->timestamp,
            'total_fee' => $this->total_fee,
            'trade_type' => $this->trade_type,
            'key' => $this->key,
        ];
        $string = http_build_query($build);
        $string = str_replace('%2F', '/', $string); //格式化网址
        $string = str_replace('%3A', ':', $string); //格式化网址
        $md5 = md5($string);
        $this->sign = strtoupper($md5);
    }

统一下单

文档:https://pay.weixin.qq.com/wik...
 public function unifiedorder()
    {

        $data = [
            'appid' => $this->appid,
            'body' => $this->body,
            'mch_id' => $this->mch_id,
            'nonce_str' => $this->nonce_str,
            'notify_url' => $this->notify_url,
            'openid' => $this->openid,
            'out_trade_no' => $this->out_trade_no,
            'sign' => $this->sign,
            'sign_type' => 'MD5',
            'spbill_create_ip' => $this->spbill_create_ip,
            'timeStamp' => $this->timestamp,
            'total_fee' => $this->total_fee * 1,
            'trade_type' => $this->trade_type,
        ];
        $xml = arrayToXml($data);
        $result = http_post(self::UNIFIEDORDER, $xml);
        $return = xmlToArray($result);
        $this->package = 'prepay_id=' . $return['prepay_id'];
        $this->renCreatesign();//这是二次签名。文档里面我是没有看到,反正我是卡到这里了。
        $returns = [
            'appid' => $this->appid,
            'noncestr' => $this->nonce_str,
            'signtype' => $this->sign_type,
            'package' => $this->package,
            'sign' => $this->resign,
            'timestamp' => $this->timestamp,
        ];
        return $returns;
    }

统一下单请忽略的所有的回调参数,只要prepay_id,其它的参数暂时看做障眼法,获取到了统一下单,还需要进行二次签名,上面代码里面有一个$this->renCreatesign(),就是调用的二次签名方法

二次签名

文档:https://pay.weixin.qq.com/wik...

所谓的二次签名就是,appId,nonceStr,package,signType,timeStamp,key的加密。一样的签名方式,可以参考签名文档,进行签名。(上面参数已经按照ASCII进行排序,大小写也请按照给出的进行加密)
注意: package格式为prepay_id=xxxxxxxxxxxx。xxxx部分为统一下单获取的prepay_id

代码参考:

public function renCreatesign()
    {
        $build_data = [
            'appId' => $this->appid,
            'nonceStr' => $this->nonce_str,
            'package' => $this->package,
            'signType' => $this->sign_type,
            'timeStamp' => $this->timestamp,
            'key' => $this->key,
        ];
        $result = http_build_query($build_data);
        $put_data = str_replace('%3D', '=', $result); //格式化网址
        $signatrue = md5($put_data);

        $this->resign = strtoupper($signatrue);

    }

至此,所有的签名应经完成,控制器使用unifiedorder()进行参数获取。

前端

这里开始使用jsapi做支付动作

            WeixinJSBridge.invoke(
                "getBrandWCPayRequest",
                {
                  appId: res.appid, //公众号名称,由商户传入
                  timeStamp: res.timeStamp, //时间戳,自1970年以来的秒数
                  nonceStr: res.nonce_str, //随机串
                  package: res.package,
                  signType: res.signType, //微信签名方式:
                  paySign: res.sign //微信签名
                },
                function(res) {
                  alert(JSON.stringify(res));
                  if (res.err_msg == "get_brand_wcpay_request:ok") {
                    // 使用以上方式判断前端返回,微信团队郑重提示:
                    //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                  }
                }
              );

前端所有需要调用的参数均在unifiedorder()可以获取到。

这里提一下WeixinJSBridge.invokewx.chooseWXPay 的区别。WeixinJSBridge.invoke可以不用引用weixinjs便可使用,也不需要config。在安卓手机上也能回调出错误信息。wx.chooseWXPay需要引用weixinjs,也需要使用config,而且在安卓手机上面的提示特别不友好。

结语

微信支付文档说实话真的很坑 很坑。貌似写文档的小哥这天情绪不好。写出来的让人也感觉到了情绪不好。以上为本人切身操作写出的教程。如还有补充的地方可以随时留言评论。

彩蛋

文档:https://github.com/datalinkag...

下载地址:

composer require datalinkage/wxpay

李益达
166 声望7 粉丝

$_ print("独自安好");